
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <amanita/aMath.h>
#include <amanita/aRandom.h>
#include "../Page.h"
#include "CardSet.h"
#include "Card.h"



CardSet::CardSet() {
	name = 0;
	sz = 0;
	cards = 0;
	width = 0;
	height = 0;
	state = CARDSET_VISIBLE;
}

CardSet::~CardSet() {
app.printf("CardSet::~CardSet()");
	if(name) { free(name);name = 0; }
	if(cards) {
		free(cards);
		sz = 0,cards = 0;
	}
}

CardSet &CardSet::operator=(const CardSet &c) {
app.printf("CardSet::operator=()");
	name = strdup(c.name);
	sz = cap = c.sz;
	if(c.cards && c.sz) {
		if(cards) free(cards);
		cards = (Card **)malloc(cap*sizeof(Card *));
		for(int i=0; i<sz; i++) {
			cards[i] = c.cards[i];
			cards[i]->setCardSet(*this);
		}
		memcpy(cards,c.cards,sz*sizeof(Card *));
	} else cards = 0;
	width = c.width;
	height = c.height;
	state = c.state;
	return *this;
}

Card * CardSet::getCardByValue(int v) {
	for(int i=0; i<sz; i++) if(cards[i]->getValue()==v) return cards[i];
	return 0;
}

CardSet CardSet::getCards(const int *s,int n) {
app.printf("CardSet::getCards()");
	CardSet set;
	if(n==0 || n>=sz) set = *this;
	else {
		set.name = strdup(name);
		set.sz = set.cap = n;
		if(set.cards) free(set.cards);
		set.cards = (Card **)malloc(set.cap*sizeof(Card *));
		for(int i=0; i<n; i++) {
			if(s) set.cards[i] = cards[s[i]];
			else set.cards[i] = cards[i];
			set.cards[i]->setCardSet(set);
		}
		set.width = width;
		set.height = height;
		set.state = state;
	}
	return set;
}

CardSet CardSet::getCardsByValue(const int *s,int n) {
app.printf("CardSet::getCardsByValue()");
	if(n==0 || n>=sz || !s) return getCards(s,n);
	CardSet set;
	set.name = strdup(name);
	set.sz = set.cap = n;
	if(set.cards) free(set.cards);
	set.cards = (Card **)malloc(set.cap*sizeof(Card *));
	Card *c;
	for(int i=0; i<n; i++) {
		c = getCardByValue(s[i]);
		if(!c) set.sz--;
		else {
			set.cards[i] = c;
			c->setCardSet(set);
		}
	}
	set.width = width;
	set.height = height;
	set.state = state;
	return set;
}

void CardSet::paint(cairo_t *cr,GdkRectangle *rects,int nrects) {
//app.printf("CardSet::paint(cards=%p,sz=%d)",cards,sz);
	if(!cards || !sz || !isVisible()) return;
	Card *c;
	int i,n,x,y;
	double a;
	for(i=0; i<sz; i++) {
		c = cards[i];
		if(!c) continue;
		if(rects && nrects) {
			for(n=0; !c->intersects(rects[n]) && n<nrects; n++);
			if(n==nrects) continue;
		}
app.printf("CardSet::paint(x=%d,y=%d,card=%s,isReversed=%d,isHideReversed=%d)",c->getX(),c->getY(),c->getName(),c->isReversed(),c->isHideReversed());
		if(c->getImage()) {
			x = c->getX(),y = c->getY();
			if(c->isFlipped() || (c->isReversed() && !c->isHideReversed())) {
				cairo_save(cr);
				cairo_translate(cr,x,y);
				a = 0,x = 0,y = 0;
				if(c->isFlipped()) {
					if(c->isReversed() && !c->isHideReversed()) a = aMath::PI*1.5,x -= c->getHeight();
					else a = aMath::PI*0.5,y -= c->getWidth();
				} else if(c->isReversed() && !c->isHideReversed()) a += aMath::PI,x -= c->getWidth(),y -= c->getHeight();
				cairo_rotate(cr,a);
			}
			cairo_set_source_surface(cr,c->getImage(),x,y);
			cairo_paint(cr);
			if(c->isFlipped() || (c->isReversed() && !c->isHideReversed())) cairo_restore(cr);
		} else {
			Page::setColor(cr,0x000000);
			cairo_rectangle(cr,c->getX(),c->getY(),c->getWidth(),c->getHeight());
			cairo_stroke_preserve(cr);
		}
		if(c->isSelected()) {
			Page::setColor(cr,0xcc0000);
			cairo_rectangle(cr,c->getX(),c->getY(),c->getWidth(),c->getHeight());
			cairo_stroke_preserve(cr);
		}
	}
}

void CardSet::load(const char *nm) {
	FILE *fp = openFile(nm);
	readHeader(fp);
	cards = (Card **)malloc(sz*sizeof(Card *));
	readCards(fp);
	fclose(fp);
}

void CardSet::loadHeader(const char *nm) {
	FILE *fp = openFile(nm);
	readHeader(fp);
	fclose(fp);
}

void CardSet::readHeader(FILE *fp) {
	aString str;
	str.appendTab(fp);
	name = strdup(str.toCharArray());
	str.clear().appendTab(fp);
	sz = cap = str.toInt();
}

void CardSet::sort() {
	int i,n;
	Card *c;
	for(i=0; i<sz-1; i++)
		for(n=i+1; n<sz; n++)
			if(cards[n]->getValue()<cards[i]->getValue())
				c = cards[i],cards[i] = cards[n],cards[n] = c;
}

void CardSet::mix(aRandom &r,int st,int rev) {
	if(!cards || !sz) return;
	int i,j,m,n;
	Card **c = (Card **)malloc(sz*sizeof(Card *));
	memset(c,0,sz*sizeof(Card *));
	for(i=0; i<sz; i++) {
		m = n = r.roll(sz-i);
		for(j=0; j<sz; j++) {
			if(!c[j]) n--;
			if(!n) break;
		}
		c[j] = cards[i];
		c[j]->setReversed(rev? r.roll(100)<=rev : false);
	}
	free(cards);
	cards = c;
	(void)m;
}

void CardSet::reverseOrder() {
	int i;
	Card *c;
	for(i=(sz/2)-1; i>=0; i--) c = cards[i],cards[i] = cards[sz-1-i],cards[sz-1-i] = c;
}

int CardSet::moveToBottom(Card &c) {
	int i,n;
	for(i=0; cards[i]!=&c && i<sz; i++);
	if(i==sz) return -1;
	for(n=i-1; n>=0; n--) cards[n+1] = cards[n];
	cards[0] = &c;
	return i;
}

int CardSet::moveToTop(Card &c) {
	int i,n;
	for(i=0; cards[i]!=&c && i<sz; i++);
	if(i==sz) return -1;
	for(n=i+1; n<sz; n++) cards[n-1] = cards[n];
	cards[sz-1] = &c;
	return i;
}

void CardSet::add(Card &c) {
	if(sz+1>cap) cards = (Card **)realloc(cards,(++cap)*sizeof(Card *));
	cards[sz++] = &c;
}

void CardSet::remove(Card &c) { if(moveToTop(c)>=0) sz--; }

void CardSet::spreadArch(GdkRectangle &r) {
	if(!cards || sz==0) return;
	int i,j,n,ah = r.height-height;
	int hsc = (sz/2)+(sz&1),nc = aMath::sum((long)0,(long)hsc-1);
	double nx = r.width<width? 0.0 : ((double)(r.width-width)/(double)(sz-1));
	double ny = ah<0? 0.0 : ((double)ah/(double)nc);
	Card *c;
	for(i=0,n=0,j=hsc; i<sz; ) {
		c = cards[i];
		if(c) {
			c->setLocation(r.x+aMath::round(nx*i),r.y+ah-aMath::round(ny*n));
			c->makeOriginalState();
		}
		i++;
		if(i<hsc) j--,n += j;
		else if(i==hsc && !(sz&1)) j = 0;
		else j++,n -= j;
	}
}

int get_cols(int cards,int rows) { return rows<=1? cards : cards/rows+(cards%rows? 1 : 0); }
double get_n(int n,int s,int cs,int p) {
	double d = (double)(s-cs)/(double)(n<=1? 1.0 : n-1);
	if(d>(double)(cs+p)) d = cs+p;
	return d;
}

void CardSet::spreadSorted(GdkRectangle &r,int p,int fr,int to) {
app.printf("CardSet::spreadSorted(x=%d,y=%d,w=%d,h=%d,fr=%d,to=%d)",r.x,r.y,r.width,r.height,fr,to);
	if(!cards || sz==0) return;
	int i,j,n,cols,rows;
	Card *c;
	double nx,ny;
	if(fr<0) fr = sz-1-fr;
	if(fr<0 || fr>=(int)sz) fr = 0;
	if(to<0) to = sz-1-to;
	if(to<0 || to>=(int)sz) to = sz-1;
	if(fr>to) n = fr,fr = to,to = n;

	n = to-fr+1,rows = 0;
	do {
		cols = get_cols(n,++rows),nx = get_n(cols,r.width,width,p),ny = get_n(rows,r.height,height,p);
app.printf("CardSet::spreadSorted(cols=%d,rows=%d,nx=%f,ny=%f)",cols,rows,nx,ny);
	} while(nx/(double)width<ny/(double)height);

app.printf("CardSet::spreadSorted(cols=%d,rows=%d,nx=%f,ny=%f)",cols,rows,nx,ny);

	for(i=0,j=0,n=fr; n<=to; i++,n++) {
		c = getCardByValue(n);
		if(!c) continue;
		if(aMath::round((double)i*nx)+width-p>r.width) i = 0,j++;
		c->setLocation(r.x+aMath::round((double)i*nx),r.y+aMath::round((double)j*ny));
		c->makeOriginalState();
	}
}

void CardSet::setCardState(int s,bool set) {
	int i;
	for(i=0; i<sz; i++) cards[i]->setState(s,set);
}


